﻿using huqiang.Framework;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
using UnityEngine.Rendering;

/// <summary>
/// Unity 在 Xbox One、PS4、Nintendo Switch、iOS、macOS 和 tvOS（仅限 Metal）、
/// Android（仅限 Vulkan）、Windows 独立平台和 UWP（仅限 DirectX 12）上支持动态分辨率。
/// 在 Camera 组件上启用 Allow Dynamic Resolution 时，Unity 会缩放该摄像机的所有目标
///  PlayerSetting中选中 Enable Frame Timing Stats 复选框。
/// </summary>
public class FrameManager : HFBase
{
    public Action<FrameManager> StateChanged;
    protected override string TypeName { get => "FrameManager"; }
    public float AverageCpuFrameTime { get; private set; }
    public float AverageGpuFrameTime { get; private set; }
    public int AverageFPS { get; private set; }
    public int AverageGpuFPS { get; private set; }
    public int CurrentFPS { get; private set; }

    private short[] m_fpsSamples;
    /// <summary>
    /// 统计平均时长
    /// </summary>
    public float StatisticsPerTime = 1;
    /// <summary>
    /// 累计时间
    /// </summary>
    float addTime;
    uint numFrames;
    uint _maxNum = 60;
    /// <summary>
    /// 最大统计数30-180帧
    /// </summary>
    public uint maxNumFrames
    {
        set
        {
            if (value > 180)
                value = 180;
            else if (value < 30)
                value = 30;
            _maxNum = value;
        }
    }
    FrameTiming[] timings;
    GraphicsDeviceType deviceType = SystemInfo.graphicsDeviceType;
    RenderingThreadingMode rendering = SystemInfo.renderingThreadingMode;
    bool _adr;
    public bool AllowDynamicResolution { get => _adr; }
    protected override void Initial()
    {
        if (deviceType == GraphicsDeviceType.Metal
            || deviceType == GraphicsDeviceType.Vulkan
            || deviceType == GraphicsDeviceType.Direct3D12
            || deviceType == GraphicsDeviceType.XboxOne)
        {
            _adr = true;
            timings = new FrameTiming[180];
        }
        else
        {
            _adr = false;
            m_fpsSamples = new short[180];
        }
    }
    protected override void Update(float time)
    {
        CurrentFPS = (short)(Mathf.RoundToInt(1f / time));
        numFrames++;
        if (numFrames >= _maxNum)
            numFrames = _maxNum;
        addTime += time;
        if (_adr)
        {
            if (addTime >= StatisticsPerTime)
            {
                CaptureADR();
                numFrames = 0;
                addTime = 0;
                if (StateChanged != null)
                    StateChanged(this);
            }
        }
        else
        {
            m_fpsSamples[numFrames - 1] = (short)CurrentFPS;
            if (addTime >= StatisticsPerTime)
            {
                CaptureLegacy();
                numFrames = 0;
                addTime = 0;
                if (StateChanged != null)
                    StateChanged(this);
            }
        }
    }

    void CaptureADR()
    {
        FrameTimingManager.CaptureFrameTimings();
        uint count = FrameTimingManager.GetLatestTimings(numFrames, timings);
        if (count == 0)
        {
            return;
        }
        double cpuTimes = 0;
        double gpuTimes = 0;
        for (int i = 0; i < count; i++)
        {
            cpuTimes += timings[i].cpuFrameTime;
            gpuTimes += timings[i].gpuFrameTime;
        }
        AverageCpuFrameTime = (float)(cpuTimes / count);
        AverageFPS = (int)(1000 / AverageCpuFrameTime);
        AverageGpuFrameTime = (float)(gpuTimes / count);
        AverageGpuFPS = (int)(1000 / AverageGpuFrameTime);
    }
    void CaptureLegacy()
    {
        int allFps = 0;
        for (int i = 0; i < numFrames; i++)
        {
            allFps += m_fpsSamples[i];
        }
        int fps = allFps / (int)numFrames;
        float time = 1 / (float)fps;
        AverageFPS = fps;
        AverageGpuFPS = fps;
        AverageCpuFrameTime = time;
        AverageGpuFrameTime = time;
    }
}
